

type ListViewDelegate = (listView: CCListView, position: number, item: cc.Node) => cc.Node;
const { ccclass, property, requireComponent } = cc._decorator;


@ccclass
@requireComponent(cc.ScrollView)
export default class CCListView extends cc.Component {
    //预制体文件
    @property(cc.Prefab)
    prefab: cc.Prefab = null;
    @property({
        type: cc.Component.EventHandler,
        displayName: "内容处理函数"
    })
    delegate = new cc.Component.EventHandler();

    @property({
        type: cc.Component.EventHandler,
        displayName: "更新函数"
    })
    delegateUpdate = new cc.Component.EventHandler();
    //间隔
    @property
    spacing: number = 5;
    //总个数
    @property
    totalCount: number = 0;

    //缓存对象
    private buffList: cc.Node[] = []
    private scrollView: cc.ScrollView;
    private content: cc.Node;
    //当前显示的内容
    private items: any[] = [];

    //最后的位置
    private lastContentPos: number = 0;

    private contentSize: number = 0;
    //位置指针
    private position_back: number = 0;
    //位置指针
    private position_front: number = 0;

    private offset: number = 0;

    private itemSize: number = 0;

    private bufferZone: number = 0;

    start(): void {
        this.scrollView = this.getComponent(cc.ScrollView)
        if (this.scrollView && this.prefab) {
            this.content = this.scrollView.content;
            this.scrollView.node.on('scrolling', this.updateItem, this);
            this.init(this.totalCount);
        }

    }


    onDestroy(): void {
        this.clear();
    }

    scrollToOffset(offset: cc.Vec2, time: number): void {
        this.scrollView.scrollToOffset(offset, time)
    }


    //长度改变后的 重置
    reset(totalCount: number): void {
        this.scrollView.stopAutoScroll();
        //没有用到对象池，所以当内容比之前少的时候需要移除，所以就直接移除了。
        this.clear();
        this.init(totalCount);
        if (this.scrollView.vertical) {
            this.scrollView.scrollToTop();
        } else {
            this.scrollView.scrollToLeft();
        }
    }

    //主动调用更新内容
    updateItems(): void {
        if (this.delegateUpdate) {
            for (let index = 0; index < this.items.length; index++) {
                let element = this.items[index];
                this.delegateUpdate.emit([element]);
            }
        }
    }

    private clear(): void {
        if (this.items && this.items.length > 0) {
            this.items.forEach(element => {
                element.active = false;
                this.buffList.push(element);
            });
        }
        this.items.length = 0;
    }

    private getItem() {
        return this.buffList.length > 0 ? this.buffList.shift() : cc.instantiate(this.prefab)
    }

    private init(totalCount: number) {
        this.totalCount = totalCount;
        let bufferSize = 0;// 
        this.position_back = -1;
        this.position_front = 0;
        let temp = this.getItem()


        this.itemSize = 0;
        if (this.scrollView.vertical) {
            this.itemSize = (temp.height + this.spacing);
            bufferSize = this.scrollView.node.height;
        } else {
            this.itemSize = (temp.width + this.spacing);
            bufferSize = this.scrollView.node.width;
        }
        this.buffList.push(temp)
        bufferSize += this.itemSize;// 多生成一个用于平滑显示。
        this.contentSize = this.totalCount * this.itemSize + this.spacing;
        let spawnCount = bufferSize % this.itemSize == 0 ? bufferSize / this.itemSize : Math.floor(bufferSize / this.itemSize) + 1;
        if (spawnCount > this.totalCount) {
            spawnCount = this.totalCount;
        }
        if (this.scrollView.vertical) {
            this.content.height = this.contentSize;
        } else {
            this.content.width = this.contentSize;
        }
        this.scrollView.scrollTo(cc.v2(0, 1), 0);
        for (let index = 0; index < spawnCount; index++) {
            this.position_back++;
            let item: cc.Node = this.getItem()
            if (!item) {
                continue;
            }
            this.addChild(item);
            this.delegate.emit([this, this.position_back, item]);
            if (this.scrollView.vertical) {
                let posy = -item.height * (0.5 + index) - this.spacing * (index + 1);
                item.setPosition(0, posy);
            } else {
                let posx = item.width * (0.5 + index) + this.spacing * (index + 1);
                item.setPosition(posx, 0);
            }
            this.items.push(item);
        }
        this.offset = this.itemSize * this.items.length;
        this.bufferZone = bufferSize / 2;
    }

    private addChild(item) {
        if (!item.parent) {
            this.content.addChild(item);
        } else {
            item.active = true;
        }
    }

    private getPositionInView(item) { // get item position in scrollview's node space
        let worldPos = item.parent.convertToWorldSpaceAR(item.position);
        let viewPos = this.scrollView.node.convertToNodeSpaceAR(worldPos);
        return viewPos;
    }



    private updateItem() {
        let items = this.items;
        let isDown = false;
        if (this.scrollView.vertical) {
            isDown = this.scrollView.content.y < this.lastContentPos; //   
        } else {
            isDown = this.scrollView.content.x > this.lastContentPos; //
        }
        for (let i = 0; i < items.length; i++) {
            let viewPos = this.getPositionInView(items[i]);
            if (this.scrollView.vertical) {
                if (isDown) {
                    if (viewPos.y < - this.bufferZone && items[i].y + this.offset < 0) {
                        this.position_front--;
                        this.position_back--;
                        items[i].y = items[i].y + this.offset;
                        this.delegate.emit([this, this.position_front, items[i]]);
                    }
                } else {
                    if (viewPos.y > this.bufferZone && items[i].y - this.offset > -this.contentSize) {
                        this.position_front++;
                        this.position_back++;
                        items[i].y = items[i].y - this.offset;
                        this.delegate.emit([this, this.position_back, items[i]]);
                    }
                }
            } else {
                if (isDown) {
                    if (viewPos.x > this.bufferZone && items[i].x - this.offset > 0) {
                        this.position_front--;
                        this.position_back--;
                        items[i].x = items[i].x - this.offset;
                        this.delegate.emit([this, this.position_front, items[i]]);
                    }

                } else {
                    if (viewPos.x < - this.bufferZone && items[i].x + this.offset < this.contentSize) {
                        this.position_front++;
                        this.position_back++;
                        items[i].x = items[i].x + this.offset;
                        this.delegate.emit([this, this.position_back, items[i]]);
                    }
                }
            }

        }
        if (this.scrollView.vertical) {
            this.lastContentPos = this.scrollView.content.y;
        } else {
            this.lastContentPos = this.scrollView.content.x;
        }
    }
}

